/* Copyright - Apache License 2.0
 * 
 * The project "kyou" is
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *      http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.kyou.data;

import java.util.Stack;

import net.kyou.exception.KyouErr;
import net.kyou.exception.KyouException;

/**
 * 工具类 提供安全地编辑DataDocument中的内容的方法
 * 
 * @author NuclearG (<a href="mailto:nuclearg@163.com">nuclearg@163.com</a>)
 */
public class DataBuilder {
    /**
     * 
     */
    private final SchemaDocument schema;
    /**
     * 
     */
    private final DataDocument data;

    /**
     * 
     * @param schema
     */
    public DataBuilder(SchemaDocument schema) {
        this.schema = schema;
        this.data = this.create(schema);
    }

    /**
     * 
     * @param schema
     * @param data
     */
    public DataBuilder(SchemaDocument schema, DataDocument data) {
        this.schema = schema;
        this.data = this.load(data, schema);
    }

    /**
     * 获取某个指定路径的报文元素的值
     * 
     * @param path
     *            报文元素的路径
     * @return 报文元素的值
     */
    public String value(String path) {
        this.checkExists(path);

        return this.data.get(path);
    }

    /**
     * 设置某个指定路径的报文元素的值
     * 
     * @param path
     *            报文元素的路径
     * @param value
     *            报文元素的值
     */
    public void value(String path, String value) {
        this.checkExists(path);

        this.data.map.put(path, value);
    }

    /**
     * 
     * @param path
     * @return
     */
    public String add(String path) {
        this.checkExists(path);

        // 检查该路径是否指向一个数组
        if (path.endsWith("]"))
            throw new KyouException(KyouErr.Schema.Data.RequireNotArrayElementPath);
        if (path != null)
            throw new KyouException(KyouErr.Schema.Data.RequireArrayPath);

        return null;
    }

    /**
     * 
     * @param path
     * @return
     */
    public String dup(String path) {
        this.checkExists(path);

        // 检查该路径是否指向一个数组元素
        if (!path.endsWith("]"))
            throw new KyouException(KyouErr.Schema.Data.RequireArrayElementPath);
        return null;
    }

    /**
     * 
     * @param path
     */
    public void remove(String path) {
        this.checkExists(path);
    }

    /**
     * 
     * @return
     */
    public DataDocument export() {
        return this.data;
    }

    private void checkExists(String path) {
        if (this.data.get(path) == null)
            throw new KyouException(KyouErr.Schema.Data.PathNotExist);
        if (path.endsWith("[]"))
            throw new KyouException(KyouErr.Schema.Data.PathNotEditable);
    }

    /**
     * 
     * @param schema
     * @return
     */
    private DataDocument create(SchemaDocument schema) {
        final Stack<String> stack = new Stack<String>();

        final DataDocument data = new DataDocument();
        schema.forEach(new ISchemaVisitor() {
            @Override
            public void docStart(SchemaDocument doc) {
                data.map.put("#", null);
                stack.push("#");
            }

            @Override
            public void docEnd(SchemaDocument doc) {
                stack.pop();
            }

            @Override
            public void struStart(SchemaStruct stru) {
                String path = stack.peek() + "." + stru.name();

                data.map.put(path, null);
                stack.push(path);

                if (stru.attrs().get(SpecialAttributes.ARRAY).equals("true")) {
                    data.map.put(path + "[]", "1");

                    path = path + "[0]";

                    data.map.put(path, null);
                    stack.push(path);
                }
            }

            @Override
            public void struEnd(SchemaStruct stru) {
                stack.pop();

                if (stru.attrs().get(SpecialAttributes.ARRAY).equals("true"))
                    stack.pop();
            }

            @Override
            public void field(SchemaField field) {
                data.map.put(stack.peek() + "." + field.name(), "");
            }
        });
        return data;
    }

    /**
     * 
     * @param data
     * @param schema
     * @return
     */
    private DataDocument load(DataDocument data, SchemaDocument schema) {
        return data;
    }
}
